// Infrastructure/Repositories/BaseRepository.cs
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
using UniversalAdmin.Domain.Entities;
using UniversalAdmin.Domain.Repositories;
using UniversalAdmin.Infrastructure.Data;

namespace UniversalAdmin.Infrastructure.Repositories;

public abstract class BaseRepository<T> : IRepository<T> where T : EntityBase
{
  protected readonly UniversalAdminDbContext _context;
  protected readonly DbSet<T> _dbSet;

  protected BaseRepository(UniversalAdminDbContext context)
  {
    _context = context;
    _dbSet = context.Set<T>();
  }

  public async Task<T?> GetByIdAsync(int id)
  {
    return await _dbSet.FindAsync(id);
  }

  public async Task<IEnumerable<T>> GetAllAsync()
  {
    return await _dbSet.ToListAsync();
  }

  public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate)
  {
    return await _dbSet.Where(predicate).ToListAsync();
  }

  public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate, CancellationToken ct)
    => await _dbSet.Where(predicate).ToListAsync(ct);

  public async Task AddAsync(T entity)
  {
    await _dbSet.AddAsync(entity);
  }

  public async Task AddRangeAsync(IEnumerable<T> entities)
  {
    await _dbSet.AddRangeAsync(entities);
  }

  public async Task UpdateAsync(T entity)
  {
    _dbSet.Update(entity);
    await Task.CompletedTask;
  }

  public async Task UpdateRangeAsync(IEnumerable<T> entities)
  {
    _dbSet.UpdateRange(entities);
    await Task.CompletedTask;
  }

  public async Task RemoveAsync(T entity)
  {
    _dbSet.Remove(entity);
    await Task.CompletedTask;
  }

  public async Task RemoveRangeAsync(IEnumerable<T> entities)
  {
    _dbSet.RemoveRange(entities);
    await Task.CompletedTask;
  }

  public async Task<bool> AnyAsync(Expression<Func<T, bool>> predicate)
  {
    return await _dbSet.AnyAsync(predicate);
  }

  public async Task<int?> CountAsync(Expression<Func<T, bool>>? predicate = null)
  {
    return predicate == null
        ? await _dbSet.CountAsync()
        : await _dbSet.CountAsync(predicate);
  }

  public async Task<(IEnumerable<T> Items, int TotalCount)> GetPagedAsync(
      Expression<Func<T, bool>>? predicate = null,
      Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null,
      int pageIndex = 1,
      int pageSize = 10,
      params string[] includeProperties)
  {
    IQueryable<T> query = _dbSet;

    if (predicate != null)
    {
      query = query.Where(predicate);
    }

    foreach (var includeProperty in includeProperties)
    {
      query = query.Include(includeProperty);
    }

    var totalCount = await query.CountAsync();

    if (orderBy != null)
    {
      query = orderBy(query);
    }

    var items = await query
        .Skip((pageIndex - 1) * pageSize)
        .Take(pageSize)
        .ToListAsync();

    return (items, totalCount);
  }

}